package com.axonactive.yolo.service.implement;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.axonactive.yolo.dao.interfaces.SubscribedFlightRepository;
import com.axonactive.yolo.dao.interfaces.SubscribedFlightUserIdRepository;
import com.axonactive.yolo.domain.SubscribedFlight;
import com.axonactive.yolo.domain.SubscribedFlightUserId;
import com.axonactive.yolo.dto.AirportArrival;
import com.axonactive.yolo.dto.AirportDeparture;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.Parameters;
import com.axonactive.yolo.exception.SubcribedFlightNotFoundException;
import com.axonactive.yolo.service.interfaces.SubscribedFlightService;
import com.axonactive.yolo.util.CONSTANTS;

@Service
public class SubscribedFlightServiceImpl implements SubscribedFlightService {
	@Resource
	private SubscribedFlightRepository subcribedFlightRepository;

	@Resource
	private SubscribedFlightUserIdRepository subscribedFlightUserIdRepository;

	@Transactional
//	@CacheEvict(value = "listSubcribedFlightCache", allEntries = true)
	public List<SubscribedFlight> findAll() {
		return subcribedFlightRepository.findAll();
	}

	@Transactional
	public SubscribedFlight create(SubscribedFlight subcribedFlight, int userId) {
		// create new subcribedFlightUserId
		SubscribedFlightUserId subcribedFlightUserId = new SubscribedFlightUserId();
		subcribedFlightUserId.setUserId(userId);
		subcribedFlightUserId.setFlightId(subcribedFlight.getFlightId());

		// always
		subscribedFlightUserIdRepository.save(subcribedFlightUserId);

		// catch if SubscribedFlight existed yet
		SubscribedFlight temp = subcribedFlightRepository
				.findByFlightId(subcribedFlight.getFlightId());
		if (temp == null) {
			subcribedFlightRepository.save(subcribedFlight);
		}

		return null;
	}

	@Transactional(rollbackFor = SubcribedFlightNotFoundException.class)
	public SubscribedFlight delete(int id)
			throws SubcribedFlightNotFoundException {
		SubscribedFlight deletedSubcribedFlight = subcribedFlightRepository
				.findOne(id);

		if (deletedSubcribedFlight == null)
			throw new SubcribedFlightNotFoundException(
					"Subcribed Flight NotFoundException");

		subcribedFlightRepository.delete(deletedSubcribedFlight);
		return deletedSubcribedFlight;
	}

	@Transactional(rollbackFor = SubcribedFlightNotFoundException.class)
	public SubscribedFlight update(SubscribedFlight subcribedFlight)
			throws SubcribedFlightNotFoundException {

		SubscribedFlight updatedSubcribedFlight = subcribedFlightRepository
				.findOne(subcribedFlight.getId());

		if (updatedSubcribedFlight == null)
			throw new SubcribedFlightNotFoundException(
					"updated Subcribed Flight NotFoundException");

		// updatedSubcribedFlight.setClassType(subcribedFlight.getClassType());
		// updatedSubcribedFlight.setCurrencyCode(subcribedFlight.getCurrencyCode());
		// updatedSubcribedFlight.setOutBoundNumStops(subcribedFlight.getOutBoundNumStops());
		// updatedSubcribedFlight.setOutBoundDuration(subcribedFlight.getOutBoundDuration());
		// updatedSubcribedFlight.setOutBoundDeparureAirportsCode(subcribedFlight.getOutBoundDeparureAirportsCode());
		// updatedSubcribedFlight.setOutBoundDepartureAirportsFlightNumber(subcribedFlight.getOutBoundDepartureAirportsFlightNumber());
		// updatedSubcribedFlight.setOutBoundDepartureAirportsDepartureTime(subcribedFlight.getOutBoundDepartureAirportsDepartureTime());
		// updatedSubcribedFlight.setOutBoundArrivalAirportsCode(subcribedFlight.getOutBoundArrivalAirportsCode());
		// updatedSubcribedFlight.setOutBoundArrivalAirportsFlightNumber(subcribedFlight.getOutBoundArrivalAirportsFlightNumber());
		// updatedSubcribedFlight.setOutBoundArrivalAirportsArrivalTime(subcribedFlight.getOutBoundArrivalAirportsArrivalTime());
		//
		// updatedSubcribedFlight.setInBoundNumStops(subcribedFlight.getInBoundNumStops());
		// updatedSubcribedFlight.setInBoundDuration(subcribedFlight.getInBoundDuration());
		// updatedSubcribedFlight.setInBoundDeparureAirportsCode(subcribedFlight.getInBoundDeparureAirportsCode());
		// updatedSubcribedFlight.setInBoundDepartureAirportsFlightNumber(subcribedFlight.getInBoundDepartureAirportsFlightNumber());
		// updatedSubcribedFlight.setInBoundDepartureAirportsDepartureTime(subcribedFlight.getInBoundDepartureAirportsDepartureTime());
		// updatedSubcribedFlight.setInBoundArrivalAirportsCode(subcribedFlight.getInBoundArrivalAirportsCode());
		// updatedSubcribedFlight.setInBoundArrivalAirportsFlightNumber(subcribedFlight.getInBoundArrivalAirportsFlightNumber());
		// updatedSubcribedFlight.setInBoundArrivalAirportsArrivalTime(subcribedFlight.getInBoundArrivalAirportsArrivalTime());

		subcribedFlightRepository.save(updatedSubcribedFlight);
		return subcribedFlight;
	}

	@Transactional
//	@Cacheable(value = "listSubcribedFlightCache", key = "#id")
	public SubscribedFlight findById(int id) {
		return subcribedFlightRepository.findOne(id);
	}

	@Transactional
//	@Cacheable(value = "listSubcribedFlightCache", key = "#id")
	public List<SubscribedFlight> findByUserId(int id) {
		// get list of subscribed flight user id
		List<SubscribedFlightUserId> subUserIds = subscribedFlightUserIdRepository
				.findByUserId(id);

		// get list of subscribed flight
		List<SubscribedFlight> subscribedFlights = subcribedFlightRepository
				.findAll();

		Iterator<SubscribedFlight> it1 = subscribedFlights.iterator();
		while (it1.hasNext()) {
			SubscribedFlight subcribedFlight = it1.next();
			if (isContain(subUserIds, subcribedFlight.getFlightId()) == false) {
				it1.remove();
			}
		}
		Collections.sort(subscribedFlights);
		return subscribedFlights;

	}

	private boolean isContain(List<SubscribedFlightUserId> subUserIds,
			String flightId) {
		for (Iterator iterator = subUserIds.iterator(); iterator.hasNext();) {
			SubscribedFlightUserId subscribedFlightUserId = (SubscribedFlightUserId) iterator
					.next();
			if (subscribedFlightUserId.getFlightId().equals(flightId))
				return true;
		}
		return false;
	}

	public SubscribedFlight formatFlight(int userId, Flight flight,
			Parameters param) {
		SubscribedFlight subscribedFlight = new SubscribedFlight();

		subscribedFlight.setFlightId(flight.getFlightID());
		// subscribedFlight.setUserId(userId);
		subscribedFlight.setClassType(flight.getOutBound().getDepartAirports()
				.get(0).getAirline().getFlightClass());
		subscribedFlight
				.setPrice(Double.valueOf(flight.getPrice().getAmount()));
		subscribedFlight.setCurrencyCode(flight.getPrice().getCurrencyCode()
				.toUpperCase());
		subscribedFlight.setTypeOfTrip(param.getTypeOfTrip());

		// set outbound values
		subscribedFlight.setOutBoundNumStops(Integer.valueOf(flight
				.getOutBound().getNumberStops()));
		subscribedFlight.setOutBoundDuration(Integer.valueOf(flight
				.getOutBound().getTotalDuration()));
		subscribedFlight.setOutBoundCarriers(flight.getOutBound()
				.getDepartAirports().get(0).getAirline().getName());
		subscribedFlight.setOutBoundAirportsExpanded(flight.getOutBound()
				.getAirportsExpanded());

		String outBoundAirportsFlightNumber = "";
		List<AirportDeparture> departAirportsOutBound = flight.getOutBound()
				.getDepartAirports();
		for (int i = 0; i < departAirportsOutBound.size(); i++) {
			outBoundAirportsFlightNumber += "-"
					+ departAirportsOutBound.get(i).getAirline()
							.getFlightNumber();
		}
		outBoundAirportsFlightNumber = outBoundAirportsFlightNumber
				.substring(1);

		subscribedFlight.setOutBoundFlightNumber(outBoundAirportsFlightNumber);
		subscribedFlight.setOutBoundDepartureTime(departAirportsOutBound.get(0)
				.getDepartureTimeByHHmm());

		List<AirportArrival> arrivalAirportsOutBound = flight.getOutBound()
				.getArriveAirports();
		subscribedFlight.setOutBoundArrivalTime(arrivalAirportsOutBound.get(
				arrivalAirportsOutBound.size() - 1).getArrivalTimeByHHmm());
		subscribedFlight.setOutBoundDayOver(Integer.parseInt(flight
				.getOutBound().getDayOver()));
		// finish set outbound values

		// set inbound values
		if (CONSTANTS.ROUND_TRIP.equals(subscribedFlight.getTypeOfTrip())) {
			subscribedFlight.setInBoundNumStops(Integer.valueOf(flight
					.getInBound().getNumberStops()));
			subscribedFlight.setInBoundDuration(Integer.valueOf(flight
					.getInBound().getTotalDuration()));
			subscribedFlight.setInBoundCarriers(flight.getInBound()
					.getDepartAirports().get(0).getAirline().getName());
			subscribedFlight.setInBoundAirportsExpanded(flight.getInBound()
					.getAirportsExpanded());

			String inBoundAirportsFlightNumber = "";
			List<AirportDeparture> departAirportsInBound = flight.getInBound()
					.getDepartAirports();
			for (int i = 0; i < departAirportsInBound.size(); i++) {
				inBoundAirportsFlightNumber += "-"
						+ departAirportsInBound.get(i).getAirline()
								.getFlightNumber();
			}
			inBoundAirportsFlightNumber = inBoundAirportsFlightNumber
					.substring(1);

			subscribedFlight
					.setInBoundFlightNumber(inBoundAirportsFlightNumber);
			subscribedFlight.setInBoundDepartureTime(departAirportsInBound.get(
					0).getDepartureTimeByHHmm());

			List<AirportArrival> arrivalAirportsInBound = flight.getInBound()
					.getArriveAirports();
			subscribedFlight.setInBoundArrivalTime(arrivalAirportsInBound.get(
					arrivalAirportsInBound.size() - 1).getArrivalTimeByHHmm());
			subscribedFlight.setInBoundDayOver(Integer.parseInt(flight
					.getInBound().getDayOver()));
		}
		// finish set inbound values

		// set parameter values
		subscribedFlight.setOutboundCode(param.getFromLocation()
				.substring(0, 3));
		subscribedFlight.setOutboundName(flight.getOutBound()
				.getDepartAirports().get(0).getName());
		subscribedFlight.setInboundCode(param.getToLocation().substring(0, 3));
		int size = flight.getOutBound().getArriveAirports().size();
		subscribedFlight.setInboundName(flight.getOutBound()
				.getArriveAirports().get(size - 1).getName());
		subscribedFlight.setOutBoundDate(param.getOutBound());
		subscribedFlight.setInBoundDate(param.getInBound());
		subscribedFlight.setNumberAdult(Integer.parseInt(param.getNumAdult()));
		subscribedFlight.setNumberChild(Integer.parseInt(param.getNumChild()));
		subscribedFlight
				.setNumberInfant(Integer.parseInt(param.getNumInfant()));
		subscribedFlight.setNumberStops(param.getNumStop());
		subscribedFlight.setUnixDepartureTime(flight.getOutBound()
				.getDepartAirports().get(0).getDepartureTime());
		subscribedFlight.setAvailable(CONSTANTS.FLIGHT_AVAILABLE);
		subscribedFlight.setPriceProvider(flight.getProviders().get(0)
				.getLabel());
		// finish parameter values

		return subscribedFlight;
	}

	public boolean alreadyAddedSubscribeFlight(
			SubscribedFlight subcribedFlight, int userId) {
		List<SubscribedFlight> subcribedFlights = (List<SubscribedFlight>) findByUserId(userId);
		for (int i = 0; i < subcribedFlights.size(); i++) {
			if (subcribedFlights.get(i).getFlightId()
					.equals(subcribedFlight.getFlightId()))
				return true;
		}
		return false;
	}

	@Transactional(rollbackFor = SubcribedFlightNotFoundException.class)
	public SubscribedFlight updateSubscribedFlight(
			SubscribedFlight subcribedFlight)
			throws SubcribedFlightNotFoundException {
		subcribedFlightRepository.save(subcribedFlight);
		return subcribedFlight;
	}

//	@Cacheable(value = "listSubcribedFlightCache", key = "#flightId")
	public SubscribedFlight findByFlightId(String flightId) {
		return subcribedFlightRepository.findByFlightId(flightId);
	}

//	@Cacheable("subscribeFlightByAvailableCache")
	public List<SubscribedFlight> findAvailableFlights() {
		return subcribedFlightRepository.findAvailableFlights();
	}

	@Override
	public boolean compareTwoSubscribeFlight(SubscribedFlight s1,
			SubscribedFlight s2) {
		// TODO Auto-generated method stub
		return false;
	}
}